Hi there!
While building my visionOS app, I’ve encountered some strange behaviour with EnvironmentBlendingComponent. Entities using .occluded(by: .surroundings) are culled entirely whenever a Mac Virtual Display or Apple Immersive Environment is behind them, regardless of the entities’ actual depth, and even when the environment is set to coexist.
This feels like it could be a bug, although I’m fairly new to visionOS development, so I may be missing something!
In a .mixed immersive space with plain passthrough, the component behaves as I would expect: an entity is occluded accurately when a real-world object is placed in front of it.
However, when either of the following is visible behind the entity:
a Mac Virtual Display window; or
an Apple Immersive Environment enabled using the Digital Crown,
the entity carrying EnvironmentBlendingComponent disappears completely. This happens even when the entity is physically closer to the viewer than the virtual surface. An otherwise identical entity without the component remains visible.
Moving the virtual surface out of the line of sight, disabling the Immersive Environment, or removing the component immediately makes the entity visible again.
Is it intended that .occluded(by: .surroundings) treats Mac Virtual Display and Apple Immersive Environments as occluders? The documentation describes this mode as depth-based occlusion against static “real-world objects”, so it isn’t clear whether these system-rendered virtual surfaces should participate.
Even if they are intended to participate, the observed behaviour appears depth-independent: the entire entity is hidden when the virtual surface is behind it, rather than only when the surface is closer to the viewer. That seems inconsistent with the documented depth-based behaviour.
I can reproduce this on an M5 Apple Vision Pro running visionOS 26.5 using the following minimal example:
import SwiftUI
import RealityKit
import UIKit
@main
struct OcclusionReproApp: App {
var body: some SwiftUI.Scene {
WindowGroup {
OcclusionReproLauncher()
}
ImmersiveSpace(id: "immersive") {
RealityView { content in
// Control: opaque, no component — proves the bare setup renders.
let control = ModelEntity(
mesh: .generateSphere(radius: 0.12),
materials: [SimpleMaterial(color: .green, isMetallic: false)]
)
control.position = [-0.25, 1.2, -1.3] // ~1.3 m ahead, eye height
// Under test: identical opaque sphere plus the occlusion component.
let occluded = ModelEntity(
mesh: .generateSphere(radius: 0.12),
materials: [SimpleMaterial(color: .red, isMetallic: false)]
)
occluded.position = [0.25, 1.2, -1.3]
occluded.components.set(
EnvironmentBlendingComponent(preferredBlendingMode: .occluded(by: .surroundings))
)
content.add(control)
content.add(occluded)
}
}
.immersionStyle(selection: .constant(.mixed), in: .mixed)
// Let the app's mixed space coexist with an Apple Immersive Environment,
// as visionOS suppresses Environments while an immersive space is open otherwise.
// The same bug can be reproduced with just a Mac Virtual Display, but
// this provides a second way to reproduce the same behaviour:
.immersiveEnvironmentBehavior(.coexist)
}
}
struct OcclusionReproLauncher: View {
@Environment(\.openImmersiveSpace) private var openImmersiveSpace
var body: some View {
Button("Open immersive space") {
Task { await openImmersiveSpace(id: "immersive") }
}
.padding()
}
}
To reproduce (on a Vision Pro, as the Simulator won't provide passthrough):
Open the immersive space in plain passthrough. Both spheres should be visible.
Place a real object in front of the red sphere. It should be occluded correctly according to depth.
Open a Mac Virtual Display or enable an Apple Immersive Environment, with its visible surface behind the red sphere.
The red sphere disappears entirely, despite being in front of that surface. The green control sphere remains visible.
Move the virtual surface out of view or disable it, and the red sphere reappears.
Please let me know if this is expected behaviour, or if I'm doing something wrong - thanks!
Jack
1
0
36